package user32

import (
	"fmt"
	"strings"
	"syscall"

	"gitee.com/kitfast/win32/a"
	"gitee.com/kitfast/win32/h"
)

// 窗体/控件树
type WindowTree struct {
	HWND     h.HWND
	Text     string
	Class    string
	Children []*WindowTree
}

func (wt *WindowTree) str(level int) string {
	sb := &strings.Builder{}
	fmt.Fprintf(sb, "hwnd: %x, class: %s, text: %s\n", wt.HWND, wt.Class, wt.Text)
	for _, c := range wt.Children {
		fmt.Fprintf(sb, "%s%s", strings.Repeat(" ", level*2), c.str(level+1))
	}
	return sb.String()
}

func (wt *WindowTree) String() string {
	return wt.str(1)
}

// 检索其类名和窗口名称与指定字符串匹配的顶级窗口的句柄。 此函数不搜索子窗口。 此函数不执行区分大小写的搜索。
// 若要从指定的子窗口开始搜索子窗口，请使用 FindWindowEx 函数。
//
// HWND FindWindowW(
//
//	[in, optional] LPCWSTR lpClassName,
//	[in, optional] LPCWSTR lpWindowName
//
// );
func FindWindow(className, windowName string) h.HWND {
	return h.HWND(a.R1(findWindow, a.OfStr0(className), a.OfStr0(windowName)))
}

// 通过窗口标题查找指定字符串匹配的顶级窗口的句柄。 此函数不搜索子窗口。 此函数不执行区分大小写的搜索。
func FindWindowByTitle(windowName string) h.HWND {
	return FindWindow("", windowName)
}

// 检索其类名和窗口名称与指定字符串匹配的窗口的句柄。
// 函数搜索子窗口，从指定子窗口后面的子窗口开始。 此函数不执行区分大小写的搜索。
//
// HWND FindWindowExW(
//
//	[in, optional] HWND    hWndParent,
//	[in, optional] HWND    hWndChildAfter,
//	[in, optional] LPCWSTR lpszClass,
//	[in, optional] LPCWSTR lpszWindow
//
// );
func FindWindowEx(parent, after h.HWND, className, windowName string) h.HWND {
	return h.HWND(a.R1(findWindowEx, parent.P(), after.P(), a.OfStr0(className), a.OfStr0(windowName)))
}

// 查找窗口树
func FindWindowTree(root h.HWND) *WindowTree {
	wt := &WindowTree{HWND: root, Class: GetClassName(root), Text: GetWindowTextByMsg(root)}
	ch := FindWindowEx(root, 0, "", "")
	if ch != 0 {
		wt.Children = make([]*WindowTree, 0)
	}
	for ch != 0 {
		wt.Children = append(wt.Children, FindWindowTree(ch))
		ch = FindWindowEx(root, ch, "", "")
	}
	return wt
}

// 更改子窗口、弹出窗口或顶级窗口的大小、位置和 Z 顺序。 这些窗口根据其在屏幕上的外观进行排序。
// 最上面的窗口接收最高排名，是 Z 顺序中的第一个窗口。
//
// BOOL SetWindowPos(
//
//	[in]           HWND hWnd,
//	[in, optional] HWND hWndInsertAfter,
//	[in]           int  X,
//	[in]           int  Y,
//	[in]           int  cx,
//	[in]           int  cy,
//	[in]           UINT uFlags
//
// );
func SetWindowPos(hWnd, hWndInsertAfter h.HWND, x, y, cx, cy int32, uFlags uint32) bool {
	return a.R1B(setWindowPos, hWnd.P(), hWndInsertAfter.P(), uintptr(x), uintptr(y), uintptr(cx), uintptr(cy), uintptr(uFlags))
}

// 窗口置顶/取消置顶
func SetWindowPosTopMost(hWnd h.HWND, topMost bool) bool {
	if topMost {
		return SetWindowPos(hWnd, h.HWND_TOPMOST, 0, 0, 0, 0, h.SWP_NOSIZE|h.SWP_NOMOVE)
	}
	return SetWindowPos(hWnd, h.HWND_NOTOPMOST, 0, 0, 0, 0, h.SWP_NOSIZE|h.SWP_NOMOVE)
}

// 窗口或控件所属的类的名称。
//
// int GetClassNameW(
//
//	[in]  HWND   hWnd,
//	[out] LPWSTR lpClassName,
//	[in]  int    nMaxCount
//
// );
func GetClassName(hWnd h.HWND) string {
	name := make([]uint16, 256)
	n := a.R1(getClassNameW, hWnd.P(), a.OfSlice(name), uintptr(256))
	return syscall.UTF16ToString(name[:n])
}

// 窗口或控件的文本。其它进程的控件可能无法获得，此时可使用 GetWindowTextByMsg 通过发送消息的方法获得。
//
// int GetWindowTextW(
//
//	[in]  HWND   hWnd,
//	[out] LPWSTR lpString,
//	[in]  int    nMaxCount
//
// );
func GetWindowText(hWnd h.HWND) string {
	buf := make([]uint16, 256)
	n := a.R1(getWindowTextW, hWnd.P(), a.OfSlice(buf), uintptr(256))
	return syscall.UTF16ToString(buf[:n])
}

// 通过发送消息的方法获得窗口或控件的文本
func GetWindowTextByMsg(hWnd h.HWND) string {
	size := SendMessage(hWnd, h.WM_GETTEXTLENGTH, 0, 0)
	if size <= 0 {
		return ""
	}
	buf := make([]uint16, size+1)
	n := SendMessage(hWnd, h.WM_GETTEXT, uintptr(size+1), a.OfSlice(buf))
	return syscall.UTF16ToString(buf[:n])
}

// 获得窗口或控件的菜单，如果失败则返回 0
func GetWindowMenu(hWnd h.HWND) h.HWND {
	return h.HWND(a.R1(sendMessageW, h.MN_GETHMENU, 0, 0))
}
